#define _CRT_SECURE_NO_WARNINGS

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <defines.h>
#include <constant.h>
#include <bingo.h>
#include <impl.h>


int harness(const char* fp)
{
    FILE *f = fopen(fp, "r");
    if (!f)
    {
        printf("error at fopen, errno: %d\n", errno);
        return -1;
    }

    char buf[0x100];
    memset(buf, 0, sizeof(buf));
    int size = fread(buf, sizeof(char), sizeof(buf), f);
    fclose(f);
    f = NULL;

    int digits = atoi(buf);
    printf("digits from file: %d \n", digits);

    return harness_inner(buf, size, digits);
}

int harness_inner(const char* buf, size_t size, int digits)
{
    bingo(buf, size);

    switch (digits)
    {

    case 1234:
    {
        printf("*(int *)(0x80000000) = %d\n", *(int *)(0x80000000));
        printf("digits / 0 = %d\n", digits / gZero);
    }
    break;

    case 1235: // Stack Buffer Overrun (/GS Exception)
    {
        char overflow[10];
        // for (unsigned int i = 1; i > 0; i++)
        // {
        //     overflow[i] = 0;
        // }
    }
    break;

    case 1236: // User Mode Write AV
    {
        *(int *)(0x80000000) = 0x12345678;
        *(int *)(0x401000) = 0x12345678;
    }
    break;

    case 1237:
    {
        typedef void(* FnInvalid)();
        auto fn = (FnInvalid)0x4;
        fn();
    }
    break;

    case 1238:
    {
        class A
        {
        public:
            ~A() {}

            virtual void fn1() {}
            virtual void fn2() {}
            virtual void fn3() {}

        private:
            int x = 1;
            int y = 2;
        };

        class B : public A
        {
        public:
            ~B() {}

            virtual void fn1() {}
            virtual void fn2() {}
            virtual void fn3() {}

        private:
            int x = 3;
            int y = 4;
        };

        auto a = new B;
        a->fn1();
        a->fn2();
        a->A::fn2();
        a->fn3();

        unsigned long long p_vfptr[3];
        if (sizeof(void*) == 8) {
            auto p8_vfptr = (unsigned long long*)p_vfptr;
            auto vfptr = *(unsigned long long**)a;
            p8_vfptr[0] = vfptr[0];
            p8_vfptr[1] = vfptr[1];
            p8_vfptr[2] = vfptr[2];
        }
        else {
            auto p4_vfptr = (unsigned int*)p_vfptr;
            auto vfptr = *(unsigned long long**)a;
            p4_vfptr[0] = vfptr[0];
            p4_vfptr[1] = vfptr[1];
            p4_vfptr[2] = vfptr[2];
        }
        *(unsigned long long**)a = p_vfptr;
        p_vfptr[1] = 4;

        a->fn1();
        a->fn2();
        a->A::fn2();
        a->fn3();

        delete a;
    }
    break;

    case 1239:  // heap-use-after-free
    {
        class A
        {
        public:
            ~A() {}

            virtual void fn1() {}
            virtual void fn2() {}
            virtual void fn3() {}

        private:
            int x = 1;
            int y = 2;
        };

        auto a = new A;
        a->fn1();
        a->fn2();
        a->fn3();
        delete a;
        a->fn1();
        a->fn2();
        a->fn3();
    }
    break;

    case 88:
        return 88;

    case -88:
        return -88;

    default:
        break;
    }

    return 0;
}